package com.sina.shop.reflect;

import com.sina.shop.core.exception.ShopCustomException;
import com.sina.shop.util.ResultCode;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * created  on 19/05/2017.
 */
public abstract class SinaReflectionUtils extends ReflectionUtils {

    private SinaReflectionUtils(){

    }

    /**
     *
     * @return public 修饰的 setter 方法则返回 true
     */
    public static boolean isSetter(Method method){
        String name = method.getName();
        return Modifier.isPublic(method.getModifiers())
                && name.startsWith("set")
                && name.length() > 3
                && Character.isUpperCase(name.charAt(3))
                && method.getParameterCount() == 1;
    }

    /**
     *
     * @return public 修饰的 getter 方法则返回 true
     */
    public static boolean isGetter(Method method){
        String name = method.getName();
        return Modifier.isPublic(method.getModifiers())
                && name.startsWith("get")
                && name.length() > 3
                && Character.isUpperCase(name.charAt(3))
                && method.getParameterCount() == 0;
    }

    /**
     * 提取 clazz 中的 public setter
     * @param clazz (not null)
     * @return 一个以 属性值为 key ,setter 为 value 的 map,且 map 是只读的
     */
    public static Map<String,Method> extractSetter(Class<?> clazz){

        Map<String,Method> map = new HashMap<>();
        ReflectionUtils.doWithMethods(clazz, method -> map.putIfAbsent(
                propertyNameFromMethod(method.getName())
                ,method), SinaReflectionUtils::isSetter);

        return Collections.unmodifiableMap(map);
    }


    /**
     * 提取 clazz 中的 public getter
     * @param clazz (not null)
     * @return 一个以 属性值为 key ,getter 为 value 的 map,且 map 是只读的
     */
    public static Map<String,Method> extractGetter(Class<?> clazz){
        Map<String,Method> map = new HashMap<>();

        ReflectionUtils.doWithMethods(clazz, method -> map.putIfAbsent(
                propertyNameFromMethod(method.getName())
                ,method), SinaReflectionUtils::isGetter);

        return Collections.unmodifiableMap(map);
    }


    /**
     * 从方法名获取属性名
     * @param methodName 方法名(getter|setter) (not null)
     * @return 属性名
     * @throws IllegalArgumentException - methodName 为 null,或长度小于 4
     */
    public static String propertyNameFromMethod(String methodName)throws IllegalArgumentException{
        if(methodName == null || methodName.length() < 4){
            throw new IllegalArgumentException(String.format("methodName[%s] Illegal",methodName));
        }
        if(methodName.length() < 5){
            return methodName.substring(3).toLowerCase();
        }else {
            return methodName.substring(3,4).toLowerCase() + methodName.substring(4);
        }

    }

    /**
     * 通过反映创建一个 {@link ShopCustomException}
     */
    public static <T> T newSinaException(Class<T> clazz, ResultCode resultCode, String message, Throwable t){
        try {
            Constructor<T> constructor = clazz.getConstructor(ResultCode.class,String.class,Throwable.class);
            return constructor.newInstance(resultCode,message,t);
        } catch (NoSuchMethodException | InstantiationException
                | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }

    }

    public static <T> T newObject(Class<T> clazz)throws RuntimeException{
        try {
           return clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean isPublicStaticMethod(Method method) {
        return method != null && Modifier.isPublic( method.getModifiers() )
                && Modifier.isStatic( method.getModifiers() );
    }

    public static String getSimpleObjectName(Object target) {
        String className;
        className = target.getClass().getSimpleName();
        if (className.length() > 1) {
            return className.substring( 0, 1 ).toLowerCase() + className.substring( 1 );
        } else {
            return className.toLowerCase();
        }
    }




}
